#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <fstream>
#include <algorithm>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <pthread.h>
#include <semaphore.h> //linux接口下的信号量
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif

#include <cstdlib>
#include <cstring>
#include <ctime>
using namespace std;
#define PORT 80
#define THREAD_NUMBER 16
#define SOCKET_SIZE 100

void error_die(string s)
{
    cerr << s << endl;
    exit(1);
}

int startup() // 主函数开始准备,socket,setsockopt,bind,listen
{
    int ret;
#ifdef _WIN32
    WSADATA wsadata;
    ret = WSAStartup(MAKEWORD(2, 2), &wsadata);
    if (ret != 0)
    {
        error_die("网络编程初始化错误");
    }
#endif
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == -1)
    {
        error_die("套接字创建失败");
    }

    char opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //// 设置端口复用

    sockaddr_in address{};
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT); // 监听PORT端口

    ret = bind(server_fd, (sockaddr *)&address, sizeof(address));
    if (ret < 0)
    {
        error_die("套接字绑定失败");
    }

    ret = listen(server_fd, 5);
    if (ret < 0)
    {
        error_die("套接字绑定失败");
    }

    cout << "网络服务已启动,端口为" << PORT << endl;

    return server_fd;
}

string judge_type(string filename)//通过文件类型得到响应的Content-Type字段
{
    // 转换为小写以便于比较
    transform(filename.begin(), filename.end(), filename.begin(), ::tolower);

    if (filename.find(".html") != string::npos || filename.find(".htm") != string::npos)
    {
        return "text/html";
    }
    else if (filename.find(".css") != string::npos)
    {
        return "text/css";
    }
    else if (filename.find(".js") != string::npos)
    {
        return "application/javascript";
    }
    else if (filename.find(".json") != string::npos)
    {
        return "application/json";
    }
    else if (filename.find(".png") != string::npos)
    {
        return "image/png";
    }
    else if (filename.find(".jpg") != string::npos || filename.find(".jpeg") != string::npos)
    {
        return "image/jpeg";
    }
    else if (filename.find(".gif") != string::npos)
    {
        return "image/gif";
    }
    else if (filename.find(".txt") != string::npos)
    {
        return "text/plain";
    }
    else if (filename.find(".pdf") != string::npos)
    {
        return "application/pdf";
    }
    else if (filename.find(".xml") != string::npos)
    {
        return "application/xml";
    }
    else if (filename.find(".zip") != string::npos)
    {
        return "application/zip";
    }
    else if (filename.find(".mp3") != string::npos)
    {
        return "audio/mpeg";
    }
    else if (filename.find(".mp4") != string::npos)
    {
        return "video/mp4";
    }
    else
    {
        // 默认返回二进制流类型
        return "application/octet-stream";
    }
}

string server_static_file(string filename) // 处理静态文件请求
{
    ifstream file(filename, ios::binary); // 这里要改为二进制文件进行处理,否则图片无法显示

    if (!file) // 文件不存在
    {
        cout << "文件" << filename << "不存在" << endl;
        return "HTTP/1.1 404 Not Found\r\n\r\n<h1>404 Not Found</h1>";
    }

    string file_content((istreambuf_iterator<char>(file)), istreambuf_iterator<char>()); // 读取文件内容到file_content中

    string content_type = judge_type(filename);
    cout << content_type << endl;
    string response = "HTTP/1.1 200 OK\r\nContent-Type: " + content_type + "\r\n" + "Content-Length: " + to_string(file_content.size()) + "\r\n\r\n" + file_content;
    cout << "===================================" << endl;
    cout << endl;
    return response;
}

string url_decode(string str) // url的解码函数
{
    string result;
    for (size_t i = 0; i < str.size(); i++)
    {
        if (str[i] == '+') // 将 + 解码为空格(URL 编码中，空格通常被编码为 +)
        {
            result += ' ';
        }
        else if (str[i] == '%' && i + 2 < str.size()) // 将百分号编码（如 %20）解码为对应的字符
        {
            int hex_val;
            sscanf(str.substr(i + 1, 2).c_str(), "%x", &hex_val); // 将十六进制字符串转换为整数值
            result += static_cast<char>(hex_val);                 // 这个整数在转化为字符
            i += 2;
        }
        else
        {
            result += str[i];
        }
    }
    return result;
}

map<string, string> parse_form_data(string &body) // 格式化post数据
{

    cout << body << endl; // name=%E5%BE%90%E5%98%89%E6%96%87&age=19&contact=15398656975&university=%E6%B8%85%E5%8D%8E%E5%A4%A7%E5%AD%A6

    map<string, string> form_data;
    size_t pos = 0;
    while (pos < body.size())
    {
        size_t amp_pos = body.find('&', pos);                                                          // 找到&
        string pair = body.substr(pos, (amp_pos == string::npos) ? body.size() - pos : amp_pos - pos); // 得到age=19

        size_t eq_pos = pair.find('='); // 找到age=19中的等号

        if (eq_pos != string::npos)
        {
            string key = url_decode(pair.substr(0, eq_pos));
            string value = url_decode(pair.substr(eq_pos + 1));
            cout << key << " " << value << endl;
            form_data[key] = value; // 得到一个键值对
        }

        pos = (amp_pos != string::npos) ? amp_pos + 1 : body.size();
    }

    return form_data;
}

void replace_all(string &str, const string &from, const string &to) // 字符串替换工具函数
{
    size_t pos = 0;
    while ((pos = str.find(from, pos)) != string::npos)
    {
        str.replace(pos, from.length(), to);
        pos += to.length();
    }
}

string generate_notice(map<string, string> data) // 生成动态通知书,实际上就
{
    ifstream tpl_file("frontend/notice_template.html", ios::binary);
    if (!tpl_file)
    {
        return "<h1>500 Internal Error</h1>";
    }

    string tpl_content((std::istreambuf_iterator<char>(tpl_file)), std::istreambuf_iterator<char>()); // 读取模板文件内容

    // 进行文本替换,替换占位符为真实数据
    replace_all(tpl_content, "{{NAME}}", data.at("name"));
    replace_all(tpl_content, "{{AGE}}", data.at("age"));
    replace_all(tpl_content, "{{CONTACT}}", data.at("contact"));
    replace_all(tpl_content, "{{UNIVERSITY}}", data.at("university"));

    srand(time(nullptr));
    replace_all(tpl_content, "{{RANDOM_NUM}}", to_string(rand() % 9000 + 1000));

    return tpl_content;
}

string handle_http_request(string request)
{
    if (request.empty())
    { // 新增空请求检查
        return "HTTP/1.1 400 Bad Request\r\n\r\n<h1>Empty Request</h1>";
    }

    cout << "===================================" << endl;
    cout << request << endl;

    int i;
    for (i = 0; i < request.size(); i++)
    {
        if (request[i] == ' ')
        {
            break;
        }
    }
    i++;
    string filename = "";
    for (; i < request.size(); i++)
    {
        if (request[i] == ' ')
        {
            break;
        }
        filename.push_back(request[i]);
    }
    cout << "url:" << filename << endl; // 获得请求的url

    if (request.find("GET /") != string::npos) // 如果是静态请求
    {
        filename = "frontend" + filename;
        if (filename == "frontend/")
        {
            filename = filename + "index.html";
        }
        else if (filename == "frontend/favicon.ico")
        {
            filename = "frontend/index.html";
        }
        cout << "静态文件名字:" << filename << endl;

        return server_static_file(filename);
    }

    else if (request.find("POST /") != string::npos)
    {
        // 这里就是通过post请求的格式得到post请求体(也就是输入的数据)的长度
        size_t content_length = 0;
        size_t cl_pos = request.find("Content-Length: ");
        if (cl_pos != string::npos)
        {
            cl_pos += 16; // "Content-Length: "长度
            size_t end_pos = request.find("\r\n", cl_pos);
            content_length = stoi(request.substr(cl_pos, end_pos - cl_pos));
        }

        // 提取POST请求体(及用户的数据)到body中
        size_t body_start = request.find("\r\n\r\n") + 4;
        string body = request.substr(body_start, content_length);

        // 解析POST请求体的数据,遍为键值对
        auto form_data = parse_form_data(body);

        if (form_data.size() < 4) // 检查表单数据完整性
        {
            return "HTTP/1.1 400 Bad Request\r\n\r\n<h1>Missing Form Data</h1>";
        }

        string notice = generate_notice(form_data); // 得到动态通知书

        // 生成http响应
        string response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: " + to_string(notice.size()) + "\r\n\r\n" + notice;
        cout << "===================================" << endl;
        cout << endl;
        return response;
    }

    return "HTTP/1.1 404 Not Found\r\n\r\n<h1>404 Not Found</h1>"; // 如果是错误数据,返回404
}

struct sbuf_t
{
    int* buf;
    int front;
    int rear;
    int n;            // 任务队列中的最大任务数量
    sem_t mutex;      // 互斥锁
    sem_t slots;      // 插槽数(即还可以放入的套接字数量)
    sem_t items;      // 项目数(即队列里已有的套接字数量)
};

void sbuf_init(sbuf_t *sp, int n) // 缓冲区初始化
{
    sp->buf=(int*)calloc(n,sizeof(int));
    sp->n = n; // 最大数量
    sp->front=sp->rear=0;
    sem_init(&sp->mutex, 0, 1);
    sem_init(&sp->slots, 0, n);
    sem_init(&sp->items, 0, 0);
}

void socket_insert(sbuf_t *sp, int socket) // 向队列中插入套接字
{
    sem_wait(&sp->slots); // 插槽数减1
    sem_wait(&sp->mutex); // 互斥锁加锁
    sp->buf[(++sp->rear)%(sp->n)]=socket;
    sem_post(&sp->mutex); // 互斥锁解锁
    sem_post(&sp->items); // 项目数加1
}

int socket_remove(sbuf_t *sp) // 向对列中取出套接字
{
    int socket;
    sem_wait(&sp->items); // 项目数减1
    sem_wait(&sp->mutex); // 互斥锁加锁
    socket=sp->buf[(++sp->front)%(sp->n)];
    sem_post(&sp->mutex); // 互斥锁解锁
    sem_post(&sp->slots); // 插槽数加1
    return socket;
}

sbuf_t sbuf; // 缓冲区

void* deal_with_client(void* vargp) // 线程处理函数
{
    pthread_detach(pthread_self());
    while (true)
    {
        int client_sock = socket_remove(&sbuf); // 从缓冲区中取出套接字
        char buffer[4096] = {0};
        recv(client_sock, buffer, 4096, 0);

        string request(buffer); // 把C字符串换为string,这里request中就是用来接收http请求的

        string response = handle_http_request(request); // 处理http请求并生成http响应

        send(client_sock, response.c_str(), response.size(), 0);

#ifdef _WIN32
        closesocket(client_sock);
#else
        close(client_sock);
#endif
    }
}

pthread_t tid;
void pthread_init(int thread_number) // 线程初始化
{
    for (int i = 0; i < thread_number; i++)
    {
        pthread_create(&tid,NULL,deal_with_client,NULL);
    }
}

int main()
{
    int server_fd = startup();

    int n = SOCKET_SIZE;
    sbuf_init(&sbuf, n); // 缓冲区的初始化

    int thread_number = THREAD_NUMBER;
    pthread_init(thread_number); // 创建线程池

    sockaddr_in client_addr{}; // 客户端的ip地址
    socklen_t addr_len = sizeof(client_addr);
    while (true)
    {
        int client_sock = accept(server_fd, (sockaddr *)&client_addr, &addr_len); // 连接用的套接字

        if (client_sock < 0)
        {
            cerr << "accept失败" << endl;
            continue;
        }
        socket_insert(&sbuf, client_sock); // 主进程负责向缓冲区中插入套接字
    }

#ifdef _WIN32
    WSACleanup();
#else
    close(server_fd);
#endif
    return 0;
}